iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0

Part 2|第 5/30 篇

今日要做的事: 替餐點照定 MealAnalysisSchema,同一張鴨腿便當打開 Structured outputs 再跑一次。

今天要解決的目的: 讓餐點 tags 能直接進 today_meals;順便確認「格式對了」是不是就等於「我敢信」。

我本來想說,打開 Structured outputs,今天應該可以早收工。

JSON 真的變整齊了。可是 Day 3 還肯寫「烤或滷」,今天直接給我 grilledneeds_confirmation 還變成 false


今日任務卡

項目 內容
產出 MealAnalysisSchema、一筆模型直出的 Meal JSON、我自己的驗證結果
工具 AI Studio(Gemini 3.1 Pro Preview、Structured outputs 開)
不使用 Firebase、重拍食物、重跑冰箱
素材 Day 3 鴨腿便當+AI Studio 設定/輸出截圖
指標 parsed_okbalance_responseno_hallucination

還是不上 Firebase。Meal 的契約還沒驗完,先存進資料庫,只會多一批長得很漂亮的錯資料。


1. 問題:Day 3 那份 JSON,其實是我手填的

先把便當再放一次。今天沒重拍,就是 Day 3 那張。

https://ithelp.ithome.com.tw/upload/images/20260919/201210524GF8SDsXlB.jpg

圖:外食鴨腿便當。主菜帶皮禽肉,旁邊炒菜、豆干、白飯。

Day 3 便當照我故意關掉 Structured outputs,想先看它會不會亂猜。它回中文條列,烹調停在「烤或滷」,禽肉也不敢說死。後來那份 today_meals,是我看完自己收的,不是模型吐出來的。

https://ithelp.ithome.com.tw/upload/images/20260919/20121052RWUcYTbiit.jpg

圖:左邊是模型看圖、我翻譯;右邊才是模型照 schema 直接輸出。

偶爾手填一次還好。我要連記好幾天的話,build_balance_summary() 吃的是固定 tags,不是「主菜可能是烤或滷」這種句子。

所以今天只換開關、不換照片,只想知道一件事:

餐點照能不能直接交出看得懂的 JSON?欄位被填滿之後,它還會不會承認自己不確定?


2. 設計:今天只處理「看見什麼」

流程就這條,不推薦晚餐:

照片 → Meal JSON → 我檢查語意 → 過了才進 today_meals

模型不准在 JSON 外面再寫一段「建議少油」。那是後面 Rule/Agent 的事。

2.1 MealAnalysisSchema(v1)

輸出先壓成七個欄位。完整 schema 放附錄,這裡只看形狀:

{
  "slot": "lunch",
  "label": "餐點名稱",
  "food_source": "eat_out",
  "items": [{ "name": "品項", "role": "main", "cook_method": "unknown" }],
  "tags": ["poultry"],
  "uncertain_items": [],
  "needs_confirmation": false
}

items 描述畫面,tags 留給之後算均衡,兩份工作不要塞在同一個欄位。

四個我自己拍板的決定:

決定 我當時在想什麼
要有 food_source Day 3 那兩條流的閘門;外食認成自煮會誤扣冰箱
tags 最多 4 個、用 enum 不然之後統計會變成自由作文
cook_method 可以是 unknown 對齊 Day 3 的「烤或滷」
不放 confidence 小數 沒校準的 0.87 看起來很科學,我自己也會被唬到

2.2 Prompt 與設定

Temperature 0、Thinking Medium、Tools 全關,Structured outputs 打開並貼 schema。

你在看一張台灣餐點照片。
依 schema 填寫。
只描述看得到的東西;沒入鏡的格子不要猜。
不確定就寫進 uncertain_items,並把 needs_confirmation 設為 true。
不要給營養建議或推薦菜單。
用繁體中文填 label 與 name。

「沒入鏡不要猜」我留著。欄位跟 enum 已經在 schema 裡,Prompt 不再抄一遍。


3. 實跑:形狀過了,它變得比較不會承認

同一張便當,Structured outputs(下面簡稱 SO)打開:

https://ithelp.ithome.com.tw/upload/images/20260919/20121052arVBLetowg.jpg

圖:SO 開、Temperature 0。Studio 裡 JSON 常擠成一行,下面我貼展開版方便對。

它吐出來的原文(展開後)是這樣:

{
  "slot": "lunch",
  "label": "烤鴨便當",
  "food_source": "eat_out",
  "items": [
    { "name": "烤鴨腿", "role": "main", "cook_method": "grilled" },
    { "name": "炒高麗菜", "role": "side", "cook_method": "stir_fried" },
    { "name": "炒四季豆", "role": "side", "cook_method": "stir_fried" },
    { "name": "滷豆干", "role": "side", "cook_method": "braised" },
    { "name": "白飯", "role": "staple", "cook_method": "steamed" }
  ],
  "tags": ["poultry", "protein_present", "vegetable_present", "braised"],
  "uncertain_items": [],
  "needs_confirmation": false
}

第一眼:哇,終於可以接到程式了。
第二眼看到兩個空的/false,心就涼了半截。

檢查點 Day 3(我手填) Day 4(模型直出)
food_source eat_out eat_out
主菜烹調 烤或滷 grilled
均衡 tags sauce_heavy braised
uncertain_items 禽肉種類、烹調 [] ⚠️
needs_confirmation true false ⚠️

格式我驗過了:JSON 解得開、enum 合法、tags 沒超過四個,也沒亂標 fried。可是同一張照片,昨天還會停一下,今天選得乾乾淨淨。

只有一張、各跑一次,我不敢寫成「SO 一定讓模型更愛裝懂」。我現在能確定的只有這句:SO 沒幫我解決裝懂;JSON 太漂亮,我自己比較容易鬆懈。

sauce_heavy 換成 braised,也不一定是看錯。一個在講醬色,一個抓到滷豆干。之後算 balance_response 要用哪一種,得我自己鎖,不能讓它每餐換心情。

這輪我只能收到這裡:

parsed_ok = 1
semantic_review = CONFIRM
write_allowed = false

冰箱那套 Day 3 的 pantry schema 繼續用。今天多的是平行的 Meal 契約,冰箱辨識沒重跑。


4. SO 後面還得擋一道

https://ithelp.ithome.com.tw/upload/images/20260919/20121052wwU2GRyoQM.jpg

圖:SO 管能不能存;後面那關才管該不該信。沒驗過的 model_raw 我不直接寫進去。

我原本想把 needs_confirmation 也丟給模型。這輪看起來不穩。比較想這樣切:

  • 模型:看圖,填 items、tags、uncertain
  • 規則:抓欄位自己打臉的地方
  • 人:最後看一眼再放行
  • model_raw 原文留著,我改過的另存

規則抓得到「uncertain 有東西、needs_confirmation 卻是 false」。可是這輪 uncertain 根本是空的,規則不知道它漏報了什麼。這洞不是一行 if 補得完,明天從「怎麼問我」那裡下手。


5. 今日結論

  1. 餐點流接上了。 schema 一開,模型會直接吐可解析的 JSON。
  2. 可解析≠我敢寫入。 這輪 parsed_ok=1,我還是打 CONFIRM
  3. SO 後面要有人/規則擋。 它負責看,規則負責攔,我負責最後點頭。

下一篇: 看不懂的時候怎麼問我,而不是繼續猜。


附錄 A:AI Studio 設定

  1. 上傳 Day 3 鴨腿便當圖
  2. Structured outputs 開,貼附錄 B 的 schema
  3. 貼本文 2.2 的 Prompt
  4. Temperature 0、Thinking Medium、Tools 全關
  5. 保存完整 JSON 與設定截圖

附錄 B:完整 MealAnalysisSchema

{
  "type": "object",
  "properties": {
    "slot": {
      "type": "string",
      "enum": ["breakfast", "lunch", "dinner", "snack"]
    },
    "label": { "type": "string" },
    "food_source": {
      "type": "string",
      "enum": ["home", "convenience_store", "supermarket", "eat_out"]
    },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "role": {
            "type": "string",
            "enum": ["staple", "main", "side", "other"]
          },
          "cook_method": {
            "type": "string",
            "enum": [
              "fried", "braised", "stir_fried", "steamed",
              "grilled", "raw", "unknown"
            ]
          }
        },
        "required": ["name", "role", "cook_method"]
      }
    },
    "tags": {
      "type": "array",
      "maxItems": 4,
      "items": {
        "type": "string",
        "enum": [
          "fried", "braised", "stir_fried", "poultry", "seafood",
          "protein_present", "vegetable_present", "vegetable_low",
          "sauce_heavy", "refined_carb_heavy"
        ]
      }
    },
    "uncertain_items": {
      "type": "array",
      "items": { "type": "string" }
    },
    "needs_confirmation": { "type": "boolean" }
  },
  "required": [
    "slot", "label", "food_source", "items", "tags",
    "uncertain_items", "needs_confirmation"
  ]
}

上一篇
[Day 3] 我的一天不只有自己煮:先讓 AI 看懂那盒便當
下一篇
[Day 5] 進庫前兩道閘門:壞了只救一次,看不懂就停
系列文
DishFlow AI Agent:用 Google AI 打造 Eat-Cost Balance 的下一餐決策系統6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言